package com.example.cycledependency;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.support.RootBeanDefinition;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class CycleDependency4 {
    private static final Map<String, RootBeanDefinition> beanDefinitionMap = new HashMap<>();
    // 一级缓存，即单例池
    private static final Map<String, Object> singletonObjects = new ConcurrentHashMap<>();
    // 二级缓存，解决getBean(beanName)方法的读写冲突
    private static final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>();
    // 三级缓存，
    private static final Map<String, ObjectFactory> singletonFactories = new ConcurrentHashMap<>();


    public static void main(String[] args) throws IllegalAccessException, InstantiationException {
        newApplicationContext();
        getBean("instanceA");
    }

    public static void newApplicationContext() throws InstantiationException, IllegalAccessException {
        // 加载所有的BeanDefinition到beanDefinitionMap中
        loadBeanDefinitions();
        // 遍历definitionMap去创建Bean
        for (String beanName : beanDefinitionMap.keySet()) {
            getBean(beanName);
        }
    }

    public static void loadBeanDefinitions() {
        RootBeanDefinition aBeanDefinition = new RootBeanDefinition(InstanceA.class);
        RootBeanDefinition bBeanDefinition = new RootBeanDefinition(InstanceB.class);
        beanDefinitionMap.put("instanceA", aBeanDefinition);
        beanDefinitionMap.put("instanceB", bBeanDefinition);
    }

    private static Object getBean(String beanName) throws IllegalAccessException, InstantiationException {
        // 若一级缓存中有，说明已创建，直接返回
        if (singletonObjects.containsKey(beanName)) {
            return singletonObjects.get(beanName);
        }
        synchronized (singletonObjects) {
            // 如果一级缓存读不到，再从二级缓存当中读
            // instanceA->instanceB，instanceB->instanceA(此时一级缓存还没有成品instanceA，要从二级缓存中读取半成品instanceA)
            if (earlySingletonObjects.containsKey(beanName)) {
                return earlySingletonObjects.get(beanName);
            }
            // 如果在二级缓存中读不到，再从三级缓存中读取，若能读到，说明出现了循环依赖的情况，这时候迫不得已提前生成代理对象，并放到二级缓存中
            if (singletonFactories.containsKey(beanName)) {
                ObjectFactory o = singletonFactories.get(beanName);
                // 创建动态代理
                Object object = o.getObject();
                // 放到二级缓存
                earlySingletonObjects.put(beanName, object);
                // 并返回该代理对象
                return object;
            }
            // 获取当前BeanDefinition
            RootBeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            // 实例化Bean
            Class<?> beanClass = beanDefinition.getClass();
            Object beanInstance = beanClass.newInstance();
            // 将创建代理对象的方法存到三级缓存中
            singletonFactories.put(beanName, new ObjectFactory() {
                @Override
                public Object getObject() throws BeansException {
                    return new JdkProxyBeanPostProcessor().getEarlyBeanReference(beanInstance, beanName);
                }
            });

            // 属性注入(循环依赖)
            // 拿到Class中的所有属性
            Field[] declaredFields = beanClass.getDeclaredFields();
            for (Field declaredField : declaredFields) {
                // 判断当前字段是否有@Autowired注解
                Autowired annotation = declaredField.getAnnotation(Autowired.class);
                // 说明属性有Autowired
                if (annotation != null) {
                    String name = declaredField.getName();
                    Object bean = getBean(name);
                    declaredField.setAccessible(true);
                    declaredField.set(beanInstance, bean);
                }
            }
            // 将成品放到一级缓存
            singletonObjects.put(beanName, beanClass);
            return beanInstance;
        }
    }
}
//为什么要包装一层ObjectFactory对象(为什么要使用第三级缓存)？
//如果创建的Bean有对应的代理，那其他对象注入时，注入的应该是对应的代理对象，但是Spring无法提前知道这个对象是不是有循环依赖的情况，而正常情况下（没有循环依赖情况），Spring都是在创建好完成品Bean之后才创建对应的代理。这时候Spring有两个选择：
//1、不管有没有循环依赖，都提前创建好代理对象，并将代理对象放入缓存，出现循环依赖时，其他对象直接就可以取到代理对象并注入。
//2、不提前创建好代理对象，在出现循环依赖被其他对象注入时，才实时生成代理对象。这样在没有循环依赖的情况下，Bean就可以按着Spring设计原则的步骤来创建。
//Spring选择了第二种方式，就是在对象外面包一层ObjectFactory，在被注入时才在ObjectFactory.getObject方式内实时生成代理对象，并将生成好的代理对象放入到第二级缓存Map<String, Object> earlySingletonObjects。
//Spring的设计原则是尽量延迟生成代理对象的时机，也就是尽量在Bean初始化完成后再生成代理对象，而不是在Bean实例化好后就生成代理对象，除非遇到循环依赖迫不得已提前生成。